home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / hplip / pcard / photocard.py < prev    next >
Text File  |  2009-10-09  |  22KB  |  742 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19.  
  20. # Std Lib
  21. import struct
  22. import time
  23. import fnmatch
  24. import mimetypes
  25. import array
  26.  
  27. # Local
  28. from base.g import *
  29. from base.codes import *
  30. from base import device, utils, exif
  31.  
  32. try:
  33.     import pcardext
  34. except ImportError:
  35.     if not os.getenv("HPLIP_BUILD"):
  36.         log.error("PCARDEXT could not be loaded. Please check HPLIP installation.")
  37.         sys.exit(1)
  38.  
  39. # Photocard command codes
  40. ACK = 0x0100
  41. NAK = 0x0101
  42. READ_CMD = 0x0010
  43. WRITE_CMD = 0x0020
  44.  
  45. SECTOR_SIZE = 512 # don't change this (TODO: impl. in pcardext)
  46.  
  47. # Photocard sector cache
  48. MAX_CACHE = 512 # units = no. sectors 
  49.  
  50. # PhotoCardFile byte cache
  51. # Used for thumbnails
  52. INITIAL_PCARDFILE_BUFFER = 20*SECTOR_SIZE 
  53. INCREMENTAL_PCARDFILE_BUFFER = 2*SECTOR_SIZE 
  54.  
  55. class PhotoCardFile:    
  56.     # File-like interface
  57.  
  58.     def __init__(self, pc, name=None):
  59.         self.pos = 0
  60.         self.closed = True
  61.         self.file_size = 0
  62.         self.pc = pc
  63.         self.buffer = array.array('c') 
  64.  
  65.         if name is not None:
  66.             self.open(name)
  67.  
  68.         self.buffer_size = INITIAL_PCARDFILE_BUFFER
  69.         self.buffer.fromstring(pcardext.read(self.name, 0, self.buffer_size))
  70.  
  71.  
  72.     def open(self, name):
  73.         self.closed = False
  74.         self.name = name
  75.  
  76.     def seek(self, offset, whence=0):
  77.         if whence == 0:
  78.             self.pos = offset
  79.         elif whence == 1:
  80.             self.pos += offset
  81.         elif whence == 2:
  82.             self.pos = self.file_size - offset
  83.         else:
  84.             return
  85.  
  86.  
  87.     def tell(self):
  88.         return self.pos
  89.  
  90.  
  91.     def read(self, size): 
  92.         if size > 0:
  93.             if self.pos + size < self.buffer_size:
  94.                 data = self.buffer[self.pos : self.pos + size].tostring()
  95.                 self.pos += size
  96.                 return data
  97.             else:
  98.                 # Read some more in from the card to satisfy the request
  99.                 while self.pos + size >= self.buffer_size:
  100.                     self.buffer.fromstring(pcardext.read(self.name, self.buffer_size, INCREMENTAL_PCARDFILE_BUFFER))
  101.                     self.buffer_size += INCREMENTAL_PCARDFILE_BUFFER
  102.                 return self.read(size)
  103.  
  104.  
  105.     def close(self):
  106.         self.closed = True
  107.         self.pos = 0
  108.  
  109.  
  110. class PhotoCard:
  111.  
  112.     def __init__(self, dev_obj=None, device_uri=None, printer_name=None):
  113.  
  114.         if dev_obj is None:
  115.             self.device = device.Device(device_uri, printer_name)
  116.             self.device.open()
  117.             self.close_device = True
  118.         else:
  119.             self.device = dev_obj
  120.             self.close_device = False
  121.  
  122.         self.dir_stack = utils.Stack()
  123.         self.current_dir = []
  124.         self.device_uri = self.device.device_uri
  125.         self.pcard_mounted = False
  126.         self.saved_pwd = []
  127.         self.sector_buffer = {}
  128.         self.sector_buffer_counts = {}
  129.         self.cache_flag = True
  130.         self.write_protect = False
  131.  
  132.         self.callback = None
  133.  
  134.         self.channel_opened = False
  135.  
  136.  
  137.     def START_OPERATION(self, name=''):
  138.         pass
  139.  
  140.     def END_OPERATION(self, name='', flag=True):
  141.         if self.channel_opened and flag:
  142.             self.close_channel()
  143.  
  144.     def set_callback(self, callback):
  145.         self.callback = callback
  146.  
  147.     def _read(self, sector, nsector): 
  148.         log.debug("read pcard sector: sector=%d count=%d" % (sector, nsector))
  149.  
  150.         if self.cache_flag:
  151.             for s in range(sector, sector+nsector):
  152.                 if s not in self.sector_buffer:
  153.                     break
  154.             else:
  155.                 buffer = ''
  156.                 for s in range(sector, sector+nsector):
  157.                     buffer = ''.join([buffer, self.sector_buffer[s]])
  158.                     log.debug("Cached sector read sector=%d" % s)
  159.                     count = self.sector_buffer_counts[s]
  160.                     self.sector_buffer_counts[s] = count+1
  161.  
  162.                     if self.callback is not None:
  163.                         self.callback()
  164.  
  165.                 #log.log_data(buffer)
  166.                 return buffer
  167.  
  168.         if self.callback is not None:
  169.             self.callback()
  170.  
  171.         if not self.channel_opened:
  172.             self.open_channel()
  173.  
  174.         log.debug("Normal sector read sector=%d count=%d" % (sector, nsector))
  175.         sectors_to_read = range(sector, sector+nsector)
  176.         request = struct.pack('!HH' + 'I'*nsector, READ_CMD, nsector, *sectors_to_read)
  177.         #log.log_data(request)
  178.         
  179.         if self.callback is not None:
  180.             self.callback()
  181.  
  182.         # send out request
  183.         bytes_written = self.device.writePCard(request)
  184.         log.debug("%d bytes written" % bytes_written)
  185.  
  186.         # read return code
  187.         data = self.device.readPCard(2)
  188.         #log.log_data(data)
  189.         code = struct.unpack('!H', data)[0]
  190.  
  191.         log.debug("Return code: %x" % code)
  192.  
  193.         if code == 0x0110:
  194.  
  195.             # read sector count and version
  196.             data = self.device.readPCard(6)
  197.             nsector_read, ver = struct.unpack('!IH', data)
  198.  
  199.             log.debug("code=0x%x, nsector=%d, ver=%d" % (code, nsector_read, ver))
  200.  
  201.             buffer, data_read, total_to_read = '', 0, nsector * SECTOR_SIZE
  202.  
  203.             while (data_read < total_to_read):
  204.                 data = self.device.readPCard(total_to_read)
  205.  
  206.                 data_read += len(data)
  207.                 buffer = ''.join([buffer, data])
  208.  
  209.                 if self.callback is not None:
  210.                     self.callback()            
  211.  
  212.             if self.cache_flag:
  213.                 i = 0
  214.  
  215.                 for s in range(sector, sector + nsector_read):
  216.                     self.sector_buffer[s] = buffer[i : i+SECTOR_SIZE]
  217.                     #log.debug("Sector %d data=\n%s" % (s, repr(self.sector_buffer[s])))
  218.                     count = self.sector_buffer_counts.get(s, 0)
  219.                     self.sector_buffer_counts[s] = count+1
  220.                     i += SECTOR_SIZE
  221.  
  222.                     if self.callback is not None:
  223.                         self.callback()            
  224.  
  225.                 self._check_cache(nsector)
  226.  
  227.             #log.log_data(buffer)
  228.             return buffer
  229.         else:
  230.             log.error("Error code: %d" % code)
  231.             return ''
  232.  
  233.     def _write(self, sector, nsector, buffer):
  234.  
  235.         #log.debug("write pcard sector: sector=%d count=%d len=%d data=\n%s" % (sector, nsector, len(buffer), repr(buffer)))
  236.         log.debug("write pcard sector: sector=%d count=%d len=%d" % (sector, nsector, len(buffer)))
  237.         
  238.         if not self.channel_opened:
  239.             self.open_channel()
  240.  
  241.  
  242.         sectors_to_write = range(sector, sector+nsector)
  243.         request = struct.pack('!HHH' + 'I'*nsector, WRITE_CMD, nsector, 0, *sectors_to_write)
  244.         request = ''.join([request, buffer])
  245.  
  246.         if self.callback is not None:
  247.             self.callback()
  248.  
  249.         self.device.writePCard(request)
  250.         data = self.device.readPCard(2)
  251.  
  252.         if self.callback is not None:
  253.             self.callback()
  254.  
  255.         code = struct.unpack('!H', data)[0]
  256.  
  257.         if code != NAK:
  258.             if self.cache_flag:
  259.                 i = 0
  260.                 for s in range(sector, sector+nsector):
  261.                     log.debug("Caching sector %d" % sector)
  262.                     self.sector_buffer[s] = buffer[i:i+SECTOR_SIZE]
  263.                     self.sector_buffer_counts[s] = 1
  264.                     i += SECTOR_SIZE
  265.  
  266.                 if self.callback is not None:
  267.                     self.callback()    
  268.  
  269.                 self._check_cache(nsector)
  270.  
  271.             return 0
  272.  
  273.         else:    
  274.             if self.cache_flag:
  275.                 for s in range(sector, sector+nsector):
  276.                     try:
  277.                         del self.sector_buffer[s]
  278.                         del self.sector_buffer_counts[s]
  279.                     except KeyError:
  280.                         pass
  281.  
  282.             log.error("Photo card write failed (Card may be write protected)")
  283.             self.close_channel()
  284.             return 1
  285.  
  286.  
  287.     def _check_cache(self, nsector):
  288.         if len(self.sector_buffer) > MAX_CACHE:
  289.             # simple minded: scan for first nsector sectors that has count of 1 and throw it away
  290.             t, n = self.sector_buffer.keys()[:], 0
  291.             for s in t:
  292.                 if self.sector_buffer_counts[s] == 1:
  293.                     del self.sector_buffer[s]
  294.                     del self.sector_buffer_counts[s]
  295.                     n += 1
  296.                     if n >= nsector:
  297.                         break
  298.                     if self.callback is not None:
  299.                         self.callback()
  300.  
  301.  
  302.  
  303.     def cache_info(self):
  304.         return self.sector_buffer_counts
  305.  
  306.     def cache_check(self, sector):
  307.         return self.sector_buffer_counts.get(sector, 0)
  308.  
  309.     def cache_control(self, control):
  310.         self.cache_flag = control
  311.  
  312.         if not self.cache_flag:
  313.             self.cache_reset()
  314.  
  315.     def cache_state(self):
  316.         return self.cache_flag
  317.  
  318.     def cache_reset(self):
  319.         self.sector_buffer.clear()
  320.         self.sector_buffer_counts.clear()
  321.  
  322.     def df(self):
  323.         df = 0
  324.         self.START_OPERATION('df')
  325.         try:
  326.             df = pcardext.df()
  327.         finally:
  328.             self.END_OPERATION('df')
  329.             return df
  330.  
  331.     def ls(self, force_read=True, glob_list='*', openclose=True):
  332.         if not glob_list:
  333.             glob_list = '*'
  334.         if force_read:
  335.             self.START_OPERATION('ls')
  336.             try:
  337.                 self.current_dir = pcardext.ls()
  338.             finally:
  339.                 self.END_OPERATION('ls', openclose)
  340.  
  341.         self.current_dir = [(n.lower(),a,s) for (n,a,s) in self.current_dir]
  342.  
  343.         if glob_list == '*':
  344.             return self.current_dir
  345.  
  346.         return [fnmatch.filter(self.current_dir, x) for x in glob_list.strip().lower().split()][0]
  347.  
  348.     def size(self, name):
  349.         for f in self.current_dir:
  350.             if f == name:
  351.                 return self.current_dir[f][2]
  352.         return 0
  353.  
  354.     def current_files(self):
  355.         return [x for x in self.current_dir if x[1] != 'd']
  356.  
  357.     def current_directories(self):
  358.         return [x for x in self.current_dir if x[1] == 'd']
  359.  
  360.     def match_files(self, glob_list):
  361.         if len(glob_list) > 0:
  362.             current_files = [x[0] for x in self.current_files()]
  363.             return [fnmatch.filter(current_files, x) for x in glob_list.strip().lower().split()][0]
  364.         return []
  365.  
  366.     def match_dirs(self, glob_list):
  367.         if len(glob_list) > 0:
  368.             current_dirs = [x[0] for x in self.current_directories()]
  369.             return [fnmatch.filter(current_dirs, x) for x in glob_list.strip().lower().split()][0]
  370.         return []
  371.  
  372.     def classify_file(self, filename):
  373.         t = mimetypes.guess_type(filename)[0]
  374.         if t is None:
  375.             return 'unknown/unknown'
  376.         return t
  377.  
  378.     # copy a single file fom pwd to lpwd
  379.     def cp(self, name, local_file, openclose=True):
  380.         self.START_OPERATION('cp')
  381.         total = 0
  382.         try:
  383.             f = file(local_file, 'w');
  384.             total = pcardext.cp(name, f.fileno())
  385.             f.close()
  386.         finally:
  387.             self.END_OPERATION('cp', openclose)
  388.             return total
  389.  
  390.     # cp multiple files in the current working directory
  391.     def cp_multiple(self, filelist, remove_after_copy, cp_status_callback=None, rm_status_callback=None):
  392.         delta, total = 0, 0
  393.         self.START_OPERATION('cp_multiple')
  394.         t1 = time.time()
  395.         try:
  396.             for f in filelist:
  397.  
  398.                 size = self.cp(f, f, False)
  399.  
  400.                 if cp_status_callback:
  401.                     cp_status_callback(os.path.join(self.pwd(), f), os.path.join(os.getcwd(), f), size)
  402.  
  403.                 total += size
  404.  
  405.  
  406.                 if remove_after_copy:
  407.                     pcardext.rm(f)
  408.  
  409.             t2 = time.time()
  410.             delta = t2-t1
  411.         finally:
  412.             if remove_after_copy:
  413.                 self.ls(True, '*', False)
  414.             self.END_OPERATION('cp_multiple')
  415.             return (total, delta)
  416.  
  417.     # cp multiple files with paths
  418.     def cp_list(self, filelist, remove_after_copy, cp_status_callback=None, rm_status_callback=None):
  419.         self.save_wd()
  420.         delta, total = 0, 0
  421.         self.START_OPERATION('cp_list')
  422.         t1 = time.time()
  423.         try:
  424.             for f in filelist:
  425.  
  426.                 path_list = f.split('/')[:-1]
  427.                 filename = f.split('/')[-1]
  428.  
  429.                 for p in path_list:
  430.                     self.cd(p, False)
  431.  
  432.                 size = self.cp(filename, filename, False)
  433.  
  434.                 if cp_status_callback is not None:
  435.                     cp_status_callback(f, os.path.join(os.getcwd(), filename), size)
  436.  
  437.                 total += size    
  438.  
  439.                 if remove_after_copy:
  440.                     pcardext.rm(filename)
  441.  
  442.                     if rm_status_callback is not None:
  443.                         rm_status_callback(f)
  444.  
  445.                 self.cd('/', False)
  446.  
  447.             t2 = time.time()
  448.             delta = t2-t1
  449.         finally:
  450.             #if remove_after_copy:
  451.             #    self.ls( True, '*', False )
  452.             self.restore_wd()
  453.             self.END_OPERATION('cp_list')
  454.             return (total, delta)
  455.  
  456.  
  457.  
  458.     def cp_fd(self, name, fd):
  459.         total = 0
  460.         self.START_OPERATION('cp_fd')
  461.         try:
  462.             total = pcardext.cp(name, fd)
  463.         finally:
  464.             self.END_OPERATION('cp_fd')
  465.             return total
  466.  
  467.  
  468.     def unload(self, unload_list, cp_status_callback=None, rm_status_callback=None, dont_remove=False):
  469.         was_cancelled = False
  470.         self.save_wd()
  471.         self.START_OPERATION('unload')
  472.         total = 0
  473.         t1 = time.time()
  474.  
  475.         for f in unload_list:
  476.             if not was_cancelled:
  477.                 name, size, typ, subtyp = f
  478.  
  479.                 p = name.split('/')
  480.                 dirs = p[:-1]
  481.                 filename = p[-1]
  482.                 self.cd('/', False)
  483.  
  484.                 if cp_status_callback is not None:
  485.                     if cp_status_callback(os.path.join(self.pwd(), filename), 
  486.                                             os.path.join(os.getcwd(), filename), 0):
  487.                         was_cancelled = True
  488.                         break
  489.  
  490.                 if len(dirs) > 0:
  491.                     for d in dirs:
  492.                         self.cd(d, False)
  493.  
  494.                 if os.path.exists(os.path.join(os.getcwd(), filename)):
  495.                     i = 2
  496.  
  497.                     while True:
  498.                         if not os.path.exists(os.path.join(os.getcwd(), filename + " (%d)" % i)):
  499.                             break
  500.  
  501.                         i += 1
  502.  
  503.                     total += self.cp(filename, filename + " (%d)" % i, False)
  504.  
  505.                 else:    
  506.                     total += self.cp(filename, filename, False)
  507.  
  508.                 if cp_status_callback is not None:
  509.                     if cp_status_callback(os.path.join(self.pwd(), filename), 
  510.                                             os.path.join(os.getcwd(), filename), size):
  511.                         was_cancelled = True
  512.                         break
  513.  
  514.                 if not dont_remove:
  515.                     if rm_status_callback is not None:
  516.                         rm_status_callback(os.path.join(self.pwd(), filename))
  517.  
  518.                     self.rm(filename, False, False)
  519.  
  520.  
  521.         t2 = time.time()
  522.         self.restore_wd(False)
  523.         self.ls(True, '*', False)
  524.         self.END_OPERATION('unload')
  525.  
  526.         return total, (t2-t1), was_cancelled
  527.  
  528.  
  529.     def get_unload_list(self):
  530.         tree = self.tree()
  531.         return self.__build_unload_list(tree)
  532.  
  533.  
  534.     def __build_unload_list(self, tree, path=None, out=None): 
  535.         if path is None:
  536.             out = []
  537.             path = utils.Stack()
  538.         for d in tree:
  539.             if type(tree[d]) == type({}):
  540.                 path.push(d)
  541.                 self.__build_unload_list(tree[d], path, out) 
  542.                 path.pop()
  543.             else:
  544.                 typ, subtyp = self.classify_file(d).split('/')
  545.                 if typ in ['image', 'audio', 'video']:
  546.                     p = path.as_list()
  547.                     name = '/'.join(['/'.join(p), d])
  548.                     out.append((name, tree[d], typ, subtyp)) 
  549.  
  550.         return out
  551.  
  552.  
  553.     def info(self):
  554.         return pcardext.info()
  555.  
  556.  
  557.     def cd(self, dirs, openclose=True):
  558.         self.START_OPERATION('cd')
  559.         try:
  560.             stat = pcardext.cd(dirs)
  561.             if stat:
  562.                 if dirs == '/':
  563.                     self.dir_stack.clear()
  564.  
  565.                 else:
  566.                     dirs = dirs.split('/')
  567.                     for d in dirs:
  568.                         self.dir_stack.push(d)
  569.  
  570.                 self.ls(True, '*', False)
  571.  
  572.         finally:
  573.             self.END_OPERATION('cd', openclose)
  574.  
  575.  
  576.     def cdup(self, openclose=True):
  577.         if len(self.dir_stack.as_list()) == 0:
  578.             return self.cd('/', openclose)
  579.  
  580.         self.dir_stack.pop()
  581.         self.START_OPERATION('cdup')
  582.         try:
  583.             pcardext.cd('/')
  584.  
  585.             for d in self.dir_stack.as_list():
  586.                 pcardext.cd(d)
  587.  
  588.             self.ls(True, '*', False)
  589.         finally:
  590.             self.END_OPERATION('cdup', openclose)
  591.  
  592.     def rm(self, name, refresh_dir=True, openclose=True):
  593.         self.START_OPERATION()
  594.         try:
  595.             r = pcardext.rm(name)
  596.  
  597.             if refresh_dir:
  598.                 self.ls(True, '*', False)
  599.         finally:
  600.             self.END_OPERATION(openclose)
  601.             return r
  602.  
  603.     def mount(self):
  604.         log.debug("Mounting photocard...")
  605.         self.START_OPERATION('mount')
  606.         try:
  607.             stat = pcardext.mount(self._read, self._write)
  608.             disk_info = pcardext.info()
  609.             self.write_protect = disk_info[8]
  610.             log.debug("stat=%d" % stat)
  611.  
  612.             if stat == 0:
  613.                 if self.write_protect:
  614.                     # if write_protect is True,
  615.                     # card write NAK'd and channel was 
  616.                     # closed. We have to reopen here.
  617.                     self.open_channel()
  618.  
  619.                 self.pcard_mounted = True
  620.                 pcardext.cd('/')
  621.  
  622.                 self.ls(True, '*', False)
  623.  
  624.             else:
  625.                 self.pcard_mounted = False
  626.                 raise Error(ERROR_DEVICE_DOES_NOT_SUPPORT_OPERATION)
  627.         finally:
  628.             if self.pcard_mounted:
  629.                 self.END_OPERATION('mount')
  630.  
  631.  
  632.  
  633.     def pwd(self):
  634.         return '/' + '/'.join(self.dir_stack.as_list())
  635.  
  636.  
  637.     def save_wd(self):
  638.         self.saved_pwd = self.dir_stack.as_list()[:]
  639.  
  640.     def restore_wd(self, openclose=True):
  641.         self.cd('/', openclose)
  642.         for d in self.saved_pwd:
  643.             self.cd(d, openclose)
  644.  
  645.  
  646.     def tree(self):
  647.         self.START_OPERATION('tree')
  648.         dir_tree = {}
  649.         try:
  650.             self.save_wd()
  651.             dir_tree = self.__tree()
  652.             self.restore_wd(False)
  653.         finally:
  654.             self.END_OPERATION('tree')
  655.             return dir_tree
  656.  
  657.     def __tree(self, __d=None):
  658.         if __d is None:
  659.             __d = {}
  660.             pcardext.cd('/')
  661.  
  662.         for f in pcardext.ls(): # True, '*', False ):
  663.             fname = f[0].lower()
  664.  
  665.             if self.callback is not None:
  666.                 self.callback()
  667.  
  668.             if fname not in ('.', '..'):
  669.                 if f[1] == 'd':
  670.                     self.cd(fname, False)
  671.                     __d[fname] = {}
  672.                     __d[fname] = self.__tree(__d[fname])
  673.                     self.cdup(False)
  674.  
  675.                 else:
  676.                     __d[fname] = f[2]
  677.  
  678.         return __d
  679.  
  680.  
  681.     def get_exif(self, name):
  682.         exif_info = {}
  683.         self.START_OPERATION('get_exif')
  684.         pcf = None
  685.         try:
  686.             pcf = PhotoCardFile(self, name)
  687.             exif_info = exif.process_file(pcf)
  688.         finally:    
  689.             if pcf is not None:
  690.                 pcf.close()
  691.             self.END_OPERATION('get_exif')
  692.             return exif_info
  693.  
  694.  
  695.     def get_exif_path(self, name):
  696.         exif_info = {}
  697.         self.START_OPERATION('get_exif_path')
  698.         self.save_wd()
  699.         try:
  700.             path_list = name.split('/')[:-1]
  701.             filename = name.split('/')[-1]
  702.  
  703.             for p in path_list:
  704.                 self.cd(p, False)
  705.  
  706.             pcf = PhotoCardFile(self, filename)
  707.             exif_info = exif.process_file(pcf)
  708.  
  709.         finally:    
  710.             self.restore_wd(False)
  711.             pcf.close()
  712.             self.END_OPERATION('get_exif_path')
  713.             return exif_info
  714.  
  715.  
  716.  
  717.     def sector(self, sector):
  718.         self.START_OPERATION('sector')
  719.         try:
  720.             data = self._read(sector, 1)
  721.         finally:
  722.             self.END_OPERATION('sector')
  723.             return data
  724.  
  725.     def umount(self):
  726.         pcardext.umount()
  727.         self.pcard_mounted = False
  728.  
  729.     def open_channel(self):
  730.         self.channel_opened = True
  731.         self.device.openPCard()
  732.  
  733.     def close_channel(self):
  734.         self.channel_opened = False
  735.         self.device.closePCard()
  736.  
  737.  
  738.  
  739.  
  740.  
  741.  
  742.